package com.dandelion.api.manager;

import com.dandelion.api.domain.*;
import com.dandelion.api.request.OrderRequest;
import com.dandelion.api.service.InMemoryDictDataScheduledService;
import com.dandelion.api.service.UserOrderService;
import com.dandelion.common.dao.auto.pojo.Order;
import com.dandelion.common.dao.auto.pojo.OrderItem;
import com.dandelion.common.domain.ExprStatusEnum;
import com.dandelion.common.domain.OrderStatusEnum;
import com.dandelion.common.service.AddressBuilder;
import com.dandelion.common.service.DictDataService;
import com.dandelion.common.utils.CommonUtils;
import com.dandelion.common.utils.MaskUtils;
import com.dandelion.common.utils.OrderNoGenerator;
import com.google.common.collect.Sets;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.stream.Collectors;

@Service
public class UserOrderManager {

    @Autowired
    private UserOrderService userOrderService;

    @Autowired
    private DictDataService dictDataService;

    @Autowired
    private InMemoryDictDataScheduledService inMemoryDictDataScheduledService;

    public Order createNewOrder(long userId, OrderRequest request) {
        Order order = toOrder(userId, request);
        userOrderService.insertNewOrder(order);

        if (CollectionUtils.isEmpty(request.getItems())) {
            return order;
        }

        request.getItems().stream().forEach(item -> {
            OrderItem orderItem = toOrderItem(order.getOrderNo(), item);
            userOrderService.insertOrderGoodsItem(orderItem);
        });
        return order;
    }

    private Order toOrder(long userId, OrderRequest request) {
        Order o = new Order();
        o.setUserId(userId);

        o.setOrderNo(OrderNoGenerator.newOrderNo());

        o.setOrderCctName(request.getOrderCctName());
        o.setOrderCctMobile(request.getOrderCctMobile());

        o.setSvrId(request.getSvrId());
        o.setTopCatId(request.getTopCatId());

        o.setCstName(request.getCstName());
        o.setCstMobile(request.getCstMobile());
        o.setCstAddrProvCode(request.getCstAddrProvCode());
        o.setCstAddrCityCode(request.getCstAddrCityCode());
        o.setCstAddrAreaCode(request.getCstAddrAreaCode());
        o.setCstAddrDistCode(request.getCstAddrDistCode());
        o.setCstAddrDetail(request.getCstAddrDetail());
        o.setBldLift(NumberUtils.toByte(request.getBldLift()));
        o.setBldFloor(request.getBldFloor());
        o.setVerify(NumberUtils.toByte(request.getVerify()));
        o.setVerifyCode(request.getVerifyCode());

        o.setExprStatus(NumberUtils.toByte(request.getExprStatus()));
        o.setExprComId(NumberUtils.toInt(request.getExprComId()));
        o.setExprOrderNo(request.getExprOrderNo());
        o.setPkCctName(request.getPkCctName());
        o.setPkCctMobile(request.getPkCctMobile());
        o.setPkAddr(request.getPkAddr());
        o.setPkgNum(NumberUtils.toInt(request.getPkgNum()));
        o.setPreFrPay(NumberUtils.toByte(request.getPreFrPay()));

        o.setSpecialReq(request.getSpecialReq());

        o.setCstExpectDeliverDate(request.getCstExpectDeliverDate());
        o.setEstDeliverDate(request.getEstDeliverDate());
        o.setStatus(OrderStatusEnum.PROCESSING.getByteValue());

        o.setCreateTime(new Date());
        return o;
    }

    private OrderItem toOrderItem(String orderNo, OrderRequest.OrderGoodsItemRequest request) {
        OrderItem item = new OrderItem();
        item.setSubCatId(request.getSubCatId());
        item.setLeafCatId(request.getLeafCatId());
        item.setOrderNo(orderNo);
        item.setImg(request.getImg());
        item.setModel(request.getModel());
        item.setWeight(request.getWeight());
        item.setHeight(request.getHeight());
        item.setLength(request.getLength());
        item.setWidth(request.getWidth());
        item.setItemNum(request.getItemNum());
        item.setReq(request.getReq());
        item.setCreateTime(new Date());
        return item;
    }

    public OrderSearchResult searchByOrderNo(String orderNo) {
        Order order = userOrderService.getOrderDetail(orderNo);
        if (order == null) {
            return null;
        }

        OrderSearchResult vo = new OrderSearchResult();
        vo.setOrderNo(orderNo);

        String cstAddrSimple = getCstAddrSimple(order);
        vo.setCstAddr(cstAddrSimple);
        vo.setStatusName(ExprStatusEnum.of(order.getExprStatus()).getStatusName());

        return vo;
    }

    public OrderAdapterVO queryOrderByNo(String orderNo) {
        Order order = userOrderService.getOrderDetail(orderNo);
        if (order == null) {
            return null;
        }

        OrderAdapterVO vo = new OrderAdapterVO(order);
        populateCstAddr(vo);
        populateSvrName(vo);
        populateExprComName(vo);
        populateOrderStatus(vo);
        populateGoodsItems(vo, orderNo);

        vo.setExprStatusName(ExprStatusEnum.of(order.getExprStatus()).getStatusName());

        populateCategoryNames(vo);

        return vo;
    }

    private void populateGoodsItems(OrderAdapterVO vo, String orderNo) {
        List<OrderItem> itemList = userOrderService.getOrderGoodsItemList(orderNo);
        if (CollectionUtils.isEmpty(itemList)) {
            return;
        }
        List<OrderItemAdapterVO> itemVoList = itemList
                .stream()
                .map(OrderItemAdapterVO::new)
                .collect(Collectors.toList());
        vo.setItems(itemVoList);
    }


    private void populateCategoryNames(OrderAdapterVO vo) {
        List<OrderItemAdapterVO> itemVoList = vo.getItems();
        Set<Integer> catIdList = new HashSet<>(CollectionUtils.size(itemVoList) * 2 + 1);

        CommonUtils.addIfUnsignedInt(catIdList, vo.getOrder().getTopCatId());

        if (CollectionUtils.isNotEmpty(itemVoList)) {
            itemVoList
                    .stream()
                    .forEach(itemVo -> {
                        CommonUtils.addIfUnsignedInt(catIdList, itemVo.getItem().getSubCatId());
                        CommonUtils.addIfUnsignedInt(catIdList, itemVo.getItem().getLeafCatId());
                    });
        }

        if (CollectionUtils.isEmpty(catIdList)) {
            return;
        }

        Map<Integer, String> catMap = dictDataService.getCategoryNameByIdListAsMap(catIdList);
        if (MapUtils.isEmpty(catMap)) {
            return;
        }

        Set<Integer> parentIdList = Sets.newHashSet(catMap.keySet());
        Map<Integer, String> catNameMap = dictDataService.getCategoryNameByIdListAsMap(parentIdList);
        if (MapUtils.isEmpty(catNameMap)) {
            return;
        }

        String topCatName = catNameMap.getOrDefault(vo.getOrder().getTopCatId(), StringUtils.EMPTY);
        vo.setTopCatName(topCatName);

        if (CollectionUtils.isNotEmpty(itemVoList)) {
            itemVoList.stream().forEach(itemVo -> {
                String catName = catNameMap.getOrDefault(itemVo.getItem().getSubCatId(), StringUtils.EMPTY);
                itemVo.setSubCatName(catName);

                catName = catNameMap.getOrDefault(itemVo.getItem().getLeafCatId(), StringUtils.EMPTY);
                itemVo.setLeafCatName(catName);
            });
        }

    }

    private void populateOrderStatus(OrderAdapterVO vo) {
        byte status = vo.getOrder().getStatus() == null ? (byte) 0 : vo.getOrder().getStatus();
        vo.setStatusName(OrderStatusEnum.of(status).getStatusName());
    }

    private void populateExprComName(OrderAdapterVO vo) {
        Integer comId = vo.getOrder().getExprComId();
        String comName = dictDataService.getExprComNameById(comId);
        vo.setExprComName(comName);
    }

    private void populateSvrName(OrderAdapterVO vo) {
        String svrName = dictDataService.getCustomServiceName(vo.getOrder().getSvrId());
        vo.setSvrName(svrName);
    }

    private String getCstAddrSimple(Order order) {
        Set<String> codeList = new HashSet<>(3);

        CommonUtils.addIf(codeList, StringUtils::isNotBlank,
                order.getCstAddrProvCode(),
                order.getCstAddrCityCode(),
                order.getCstAddrAreaCode()
        );

        Map<String, String> addrNameMap = dictDataService.getAddressNameAsMap(codeList);
        if (MapUtils.isEmpty(addrNameMap)) {
            return StringUtils.EMPTY;
        }

        String addr = AddressBuilder
                .newBuilder()
                .codeAndNameMap(addrNameMap)
                .prov(order.getCstAddrProvCode())
                .city(order.getCstAddrCityCode())
                .area(order.getCstAddrAreaCode())
                .build();
        return addr;
    }

    private void populateCstAddr(OrderAdapterVO vo) {
        Set<String> codeList = new HashSet<>(8);
        Order order = vo.getOrder();

        CommonUtils.addIf(codeList, StringUtils::isNotBlank,
                order.getCstAddrProvCode(),
                order.getCstAddrCityCode(),
                order.getCstAddrAreaCode(),
                order.getCstAddrDistCode()
        );

        Map<String, String> addrNameMap = dictDataService.getAddressNameAsMap(codeList);
        if (MapUtils.isEmpty(addrNameMap)) {
            return;
        }

        String addr = AddressBuilder
                .newBuilder()
                .codeAndNameMap(addrNameMap)
                .prov(order.getCstAddrProvCode())
                .city(order.getCstAddrCityCode())
                .area(order.getCstAddrAreaCode())
                .district(order.getCstAddrDistCode())
                .detail(order.getCstAddrDetail())
                .build();
        vo.setCstAddr(addr);
    }

    public void cancelOrder(long userId, String orderNo) {
    }


    public List<OrderListItemAdapter> getCancelledMyOrderList(long userId) {
        List<Order> orderList = userOrderService.queryMyOrderCancelled(userId);
        return toOrderListItemAdapterList(orderList);
    }

    public List<OrderListItemAdapter> getProcessingMyOrderList(long userId) {
        List<Order> orderList = userOrderService.queryMyOrderProcessing(userId);
        return toOrderListItemAdapterList(orderList);
    }

    public List<OrderListItemAdapter> getFinishedMyOrderList(long userId) {
        List<Order> orderList = userOrderService.queryMyOrderFinished(userId);
        return toOrderListItemAdapterList(orderList);
    }

    public List<OrderListItemAdapter> getCancelledOrderListByMobile(String mobile) {
        List<Order> orderList = userOrderService.getOrderListCancelled(mobile);
        return toOrderListItemAdapterList(orderList);
    }

    public List<OrderListItemAdapter> getProcessingOrderListByMobile(String mobile) {
        List<Order> orderList = userOrderService.getOrderListProcessing(mobile);
        return toOrderListItemAdapterList(orderList);
    }

    public List<OrderListItemAdapter> getFinishedOrderListByMobile(String mobile) {
        List<Order> orderList = userOrderService.getOrderListFinished(mobile);
        return toOrderListItemAdapterList(orderList);
    }

    private List<OrderListItemAdapter> toOrderListItemAdapterList(List<Order> orderList) {
        if (CollectionUtils.isEmpty(orderList)) {
            return null;
        }

        List<OrderListItemAdapter> orderListItemAdapters = new ArrayList<>(orderList.size());
        Set<Integer> catIdSet = new HashSet<>(orderList.size());
        Set<Integer> svrIdSet = new HashSet<>(orderList.size());
        Set<String> addrCodeSet = new HashSet<>(orderList.size() * 3);
        orderList
                .stream()
                .forEach(order -> {
                    orderListItemAdapters.add(new OrderListItemAdapter(order));
                    CommonUtils.addIfUnsignedInt(catIdSet, order.getTopCatId());
                    CommonUtils.addIfUnsignedInt(svrIdSet, order.getSvrId());
                    CommonUtils.addIf(addrCodeSet,
                            StringUtils::isNotBlank,
                            order.getCstAddrProvCode(),
                            order.getCstAddrCityCode(),
                            order.getCstAddrAreaCode(),
                            order.getCstAddrDistCode()
                    );
                });
        Map<String, String> addrNameMap = dictDataService.getAddressNameAsMap(addrCodeSet);
        Map<Integer, String> svrNameMap = dictDataService.getSvrNameByIdSetAsMap(svrIdSet);
        Map<Integer, String> catNameMap = dictDataService.getCategoryNameByIdListAsMap(svrIdSet);
        Map<Integer, String> exprComNameMap = dictDataService.getExprCompanyAsMap();
        orderListItemAdapters
                .stream()
                .forEach(adapter -> {
                    Order order = adapter.getOrder();
                    adapter.setStatusName(OrderStatusEnum.of(order.getStatus()).getStatusName());
                    adapter.setSvrName(MapUtils.getString(svrNameMap, order.getSvrId(), StringUtils.EMPTY));
                    adapter.setTopCatName(MapUtils.getString(catNameMap, order.getTopCatId(), StringUtils.EMPTY));
                    adapter.setExprComName(MapUtils.getString(exprComNameMap, order.getExprComId()));
                    String addr = AddressBuilder
                            .newBuilder()
                            .codeAndNameMap(addrNameMap)
                            .prov(order.getCstAddrProvCode())
                            .city(order.getCstAddrCityCode())
                            .area(order.getCstAddrAreaCode())
                            .district(order.getCstAddrDistCode())
                            .detail(order.getCstAddrDetail())
                            .build();

                    adapter.setCstAddr(addr);
                });


        return orderListItemAdapters;
    }

    public List<NewestOrder> queryNewestOrderList() {
        List<NewestOrder> orderList = userOrderService.queryNewestOrderList();
        if (CollectionUtils.isNotEmpty(orderList)) {
            Set<String> addrCodeSet = new HashSet<>(orderList.size() * 2);
            orderList
                    .stream()
                    .forEach(order -> {
                        CommonUtils.addIf(addrCodeSet,
                                StringUtils::isNotBlank,
                                order.getProvCode(),
                                order.getCityCode()
                        );
                    });

            Map<String, String> addrNameMap = dictDataService.getAddressNameAsMap(addrCodeSet);
            orderList.forEach(order -> {
                order.setStatus(ExprStatusEnum.of(order.getStatusCode()).getStatusName());
                String addr = AddressBuilder
                        .newBuilder()
                        .codeAndNameMap(addrNameMap)
                        .prov(order.getProvCode())
                        .city(order.getCityCode())
                        .build();
                order.setAddr(addr);
                order.setNo(MaskUtils.maskOrderNo(order.getNo()));
            });
        }


        return orderList;
    }
}
